﻿
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class TouchPointerEvent : MonoBehaviour
{

    private MyPointerEventData[] m_PointerData = new MyPointerEventData[5];
    private List<MyPointerEventData> m_ActivePointerDataList = new List<MyPointerEventData>();
    private List<MyPointerEventData> m_TempRemovePointerDataList = new List<MyPointerEventData>();

    private List<RaycastResult> m_RaycastResultCache = new List<RaycastResult>();
    private PointerEventData m_TempPointerEventData;

    private GameObject m_FirstRaycastGameObject = null;
    private int m_FrameInvokeCount;

    private int m_UpdateIndex;

    protected void OnPointerEnter(MyPointerEventData data)
    {
        Debug.Log($"OnPointerEnter");
    }

    protected void OnPointerExit(MyPointerEventData data)
    {
        Debug.Log($"OnPointerExit");
    }

    protected void OnPointerDown(MyPointerEventData data)
    {
        Debug.Log($"OnPointerDown");
    }

    protected void OnPointerUp(MyPointerEventData data)
    {
        Debug.Log($"OnPointerUp");
    }

    protected void OnClick(MyPointerEventData data)
    {
        Debug.Log($"OnClick");
    }

    protected void OnLongPressBegin(MyPointerEventData data)
    {
        Debug.Log($"OnLongPressBegin");
    }

    protected void OnLongPress(MyPointerEventData data)
    {
        Debug.Log($"OnLongPress");
    }

    protected void OnLongPressEnd(MyPointerEventData data)
    {
        Debug.Log($"OnLongPressEnd");
    }

    protected void OnDragBegin(MyPointerEventData data)
    {
        Debug.Log($"OnDragBegin");
    }

    protected void OnDrag(MyPointerEventData data)
    {
        //Debug.Log($"OnDrag");
    }

    protected void OnDragEnd(MyPointerEventData data)
    {
        Debug.Log($"OnDragEnd");
    }

    protected void OnPinchBegin(MyPointerEventData pointer1, MyPointerEventData pointer2s)
    {

    }

    protected void OnPinch(MyPointerEventData pointer1, MyPointerEventData pointer2, Vector2 deltaDist)
    {

    }

    protected void OnPinchEnd(MyPointerEventData pointer1, MyPointerEventData pointer2, Vector2 deltaDist, bool isCancel)
    {

    }

    private GameObject m_PinchObject = null;
    private Vector2 m_PinchPointerDist = Vector2.zero;

    void Update()
    {
        m_FirstRaycastGameObject = null;
        m_FrameInvokeCount = 0;

        for (int i = 0; i < Input.touchCount; ++i)
        {
            var touch = Input.GetTouch(i);
            if (touch.type == TouchType.Indirect) continue;
            if (touch.fingerId >= m_PointerData.Length)
            {
                Debug.LogWarning($"> maxPointer: {touch.fingerId}");
                continue;
            }

            switch (touch.phase)
            {
            case TouchPhase.Began:
                CheckTouchDown(touch);
                break;

            case TouchPhase.Stationary:
                CheckTouchStay(touch);
                break;

            case TouchPhase.Moved:
                CheckTouchMove(touch);
                break;

            case TouchPhase.Ended:
                CheckTouchEnd(touch);
                break;

            case TouchPhase.Canceled: //和end的区别就是无抬起对象
                CheckTouchCancel(touch);
                break;

            }
        }

        CheckGesture_Pinch();

        foreach (var data in m_TempRemovePointerDataList)
        {
            m_ActivePointerDataList.Remove(data);
        }

        //Thread.Sleep(1000);
        m_UpdateIndex++;
    }

    private void CheckGesture_Pinch()
    {
        if (m_ActivePointerDataList.Count > 1)
        {
            MyPointerEventData pointer1 = null;
            MyPointerEventData pointer2 = null;
            bool isFindPinchPointer = false;
            for (int i = 0; i < m_ActivePointerDataList.Count - 1; ++i)
            {
                pointer1 = m_ActivePointerDataList[i];
                for (int j = i + 1; j < m_ActivePointerDataList.Count; ++j)
                {
                    pointer2 = m_ActivePointerDataList[j];
                    if (pointer1.m_PointerDown == pointer2.m_PointerDown)
                    {
                        isFindPinchPointer = true;
                        break;
                    }
                }
                if (isFindPinchPointer) break;
            }

            if (isFindPinchPointer)
            {
                if (null != m_PinchObject)
                {
                    if (m_PinchObject != pointer1.m_PointerDown)
                    {
                        //pinch过程中, 对象变了? 暂时认为不会有这样的情况
                        Debug.LogWarning($"pinchObject change ??? {m_PinchObject.name} -> {pointer1.m_PointerDown.name}");
                    }

                    //1个手指中途抬起了, 就结束了
                    if (pointer1.m_Phase == TouchPhase.Canceled || pointer2.m_Phase == TouchPhase.Canceled)
                    {
                        m_PinchObject = null;
                        Vector2 pointerDist = pointer1.m_Position - pointer2.m_Position;
                        Vector2 deltaDist = pointerDist - m_PinchPointerDist;
                        m_PinchPointerDist = pointerDist;
                        OnPinchEnd(pointer1, pointer2, deltaDist, true);
                    }
                    else if (pointer1.m_Phase == TouchPhase.Ended || pointer2.m_Phase == TouchPhase.Ended)
                    {
                        m_PinchObject = null;
                        Vector2 pointerDist = pointer1.m_Position - pointer2.m_Position;
                        Vector2 deltaDist = pointerDist - m_PinchPointerDist;
                        m_PinchPointerDist = pointerDist;
                        OnPinchEnd(pointer1, pointer2, deltaDist, false);
                    }
                    else if (pointer1.m_Phase == TouchPhase.Stationary && pointer2.m_Phase == TouchPhase.Stationary) //两个手指都没动
                    {
                        // do nothing
                    }
                    else //一个手指动或两个手指都在动
                    {
                        Vector2 pointerDist = pointer1.m_Position - pointer2.m_Position;
                        Vector2 deltaDist = pointerDist - m_PinchPointerDist;
                        m_PinchPointerDist = pointerDist;
                        OnPinch(pointer1, pointer2, deltaDist);
                    }
                }
                else if (pointer1.m_Status == MyPointerEventData.Status.None || pointer2.m_Status == MyPointerEventData.Status.None)
                {
                    m_PinchObject = pointer1.m_PointerDown;
                    m_PinchPointerDist = pointer1.m_Position - pointer2.m_Position;
                    OnPinchBegin(pointer1, pointer2);
                }
            }
            else
            {
                if (null != m_PinchObject)
                {
                    Debug.LogWarning($"pinchObject not null !!!");
                    m_PinchObject = null;
                    //OnPinchEnd(pointer1, pointer2, deltaDist, true); //无pointer可传
                }
            }
        }
    }

    private void CheckTouchDown(Touch touch)
    {
        var pointerData = GetPointerData(touch.fingerId);
        pointerData.m_Phase = touch.phase;
        pointerData.m_Delta = Vector2.zero;
        pointerData.m_Position = touch.position;
        pointerData.m_Status = MyPointerEventData.Status.None;

        pointerData.m_DownPosition = touch.position;
        var rawPointerDown = GetFirstRaycastGameObject(pointerData.m_Position); //射线检测到的点击对象, 事件触发对象可能为Ascent
        OnPointerDown(pointerData); //todo: raycastDown对象或Ascent为down对象, down对象触发OnPointerDown

        if (rawPointerDown == pointerData.m_LastDown)
        {
            float elapsedTime = Time.unscaledTime - pointerData.m_DownTime;
            if (elapsedTime < 0.3f)
                pointerData.m_ClickCount++;
            else //超过时间就直接重置
                pointerData.m_ClickCount = 1;
        }
        else //第1次Down或换了一个GameObject Down
        {
            pointerData.m_ClickCount = 1;
        }
        pointerData.m_DownTime = Time.unscaledTime;

        pointerData.m_PointerDown = rawPointerDown; //这边暂时没区分事件触发对象和射线检测点击对象
        pointerData.m_PointerDrag = rawPointerDown;
        //todo: raycastDown对象或Ascent为drag对象, drag对象触发OnPotentialDrag
    }

    private void CheckTouchStay(Touch touch)
    {
        var pointerData = GetPointerData(touch.fingerId);
        pointerData.m_Phase = touch.phase;
        pointerData.m_Delta = touch.position - pointerData.m_Position;
        pointerData.m_Position = touch.position;
        if (pointerData.m_Delta.sqrMagnitude >= 0.001f) Debug.LogError($"Stationary: delta > 0 ???");

        switch (pointerData.m_Status)
        {
        case MyPointerEventData.Status.None:
            float elapsedTime = Time.unscaledTime - pointerData.m_DownTime;
            if (elapsedTime >= 0.3f)
            {
                OnLongPressBegin(pointerData); //todo: down对象触发OnLongPressBegin
                pointerData.m_Status = MyPointerEventData.Status.LongPress;
            }
            break;

        case MyPointerEventData.Status.LongPress:
            OnLongPress(pointerData); //todo: down对象触发OnLongPress
            break;

        case MyPointerEventData.Status.Drag:
            Debug.LogError($"will not be here !!!");
            break;
        }
    }

    private void CheckTouchMove(Touch touch)
    {
        var pointerData = GetPointerData(touch.fingerId);
        pointerData.m_Phase = touch.phase;
        pointerData.m_Delta = touch.position - pointerData.m_Position;
        pointerData.m_Position = touch.position;

        CheckEnterAndExit(pointerData);

        switch (pointerData.m_Status)
        {
        case MyPointerEventData.Status.None:
            bool isCheckLongPressBegin = true;
            if (null != pointerData.m_PointerDrag)
            {
                float dragSlop = 7;
                Vector2 deltaPos = pointerData.m_DownPosition - pointerData.m_Position;
                if (deltaPos.sqrMagnitude >= dragSlop * dragSlop)
                {
                    isCheckLongPressBegin = false;
                    OnDragBegin(pointerData); //todo: drag对象触发OnBeginDrag
                    pointerData.m_Status = MyPointerEventData.Status.Drag;

                    if (pointerData.m_PointerDown != pointerData.m_PointerDrag)
                    {
                        //todo: down对象触发OnPointerUp
                    }
                    OnDrag(pointerData); //todo: drag对象触发OnDrag
                }
            }

            if (isCheckLongPressBegin)
            {
                float elapsedTime = Time.unscaledTime - pointerData.m_DownTime;
                if (elapsedTime >= 0.3f)
                {
                    OnLongPressBegin(pointerData); //todo: down对象触发OnLongPressBegin
                    pointerData.m_Status = MyPointerEventData.Status.LongPress;
                }
            }
            break;

        case MyPointerEventData.Status.LongPress: //长按后再drag
            bool isCheckLongPress = true;
            if (null != pointerData.m_PointerDrag)
            {
                float dragSlop = 7;
                Vector2 deltaPos = pointerData.m_DownPosition - pointerData.m_Position;
                if (deltaPos.sqrMagnitude >= dragSlop * dragSlop)
                {
                    isCheckLongPress = false;
                    OnDragBegin(pointerData); //todo: drag对象触发OnBeginDrag
                    pointerData.m_Status = MyPointerEventData.Status.Drag;

                    if (pointerData.m_PointerDown != pointerData.m_PointerDrag)
                    {
                        //todo: down对象触发OnPointerUp
                    }
                    OnDrag(pointerData); //todo: drag对象触发OnDrag
                }
            }

            if (isCheckLongPress)
                OnLongPress(pointerData); //todo: down对象触发OnLongPress
            break;

        case MyPointerEventData.Status.Drag:
            OnDrag(pointerData); //todo: drag对象触发OnDrag
            break;
        }
    }

    private void CheckEnterAndExit(MyPointerEventData pointerData)
    {
        if (0 == m_UpdateIndex || (pointerData.m_Position - pointerData.m_EnterPosition).sqrMagnitude >= 0.001f) //移动: 没发生移动就不需要检测Enter对象变化
        {
            pointerData.m_EnterPosition = pointerData.m_Position;

            var firstRaycastGameObject = GetFirstRaycastGameObject(pointerData.m_Position);
            if (null == pointerData.m_PointerEnter)
            {
                if (null == firstRaycastGameObject)
                {
                    //todo: hovered上触发OnPoiterExit
                }
                else
                {
                    //todo: raycast对象为enter对象, enter对象上触发OnPointerEnter
                    pointerData.m_PointerEnter = firstRaycastGameObject;
                }
            }
            else
            {
                if (null == firstRaycastGameObject)
                {
                    //todo: 旧enter对象上触发OnPoiterExit
                    pointerData.m_PointerEnter = null;
                }
                else if (firstRaycastGameObject != pointerData.m_PointerEnter)
                {
                    //todo: 旧enter对象上触发OnPointerEixt, 新enter对象上触发OnPointerEnter
                    pointerData.m_PointerEnter = firstRaycastGameObject;
                }
            }
        }
    }

    private void CheckTouchEnd(Touch touch)
    {
        var pointerData = GetPointerData(touch.fingerId);
        pointerData.m_Phase = touch.phase;
        pointerData.m_Delta = touch.position - pointerData.m_Position;
        pointerData.m_Position = touch.position;

        var firstRaycastGameObject = GetFirstRaycastGameObject(pointerData.m_Position);
        switch (pointerData.m_Status)
        {
        case MyPointerEventData.Status.None:
            OnPointerUp(pointerData); //todo: down对象触发OnPointerUp

            if (pointerData.m_PointerDown == firstRaycastGameObject)
            {
                OnClick(pointerData); //todo: down对象触发OnClick事件, 检查clickCount
                pointerData.m_LastDown = pointerData.m_PointerDown; //用于下次Down检测双击
            }
            else
            {
                pointerData.m_LastDown = null; //不触发点击, 就没后续双击
            }
            break;

        case MyPointerEventData.Status.Drag:
            //在前面已经触发过OnPointerUp
            if (null != pointerData.m_PointerDrag)
            {
                OnDragEnd(pointerData); //todo: drag对象触发OnDragEnd
            }
            pointerData.m_LastDown = null; //drag, 下次Down没双击
            break;

        case MyPointerEventData.Status.LongPress:
            OnPointerUp(pointerData); //todo: down对象触发OnPointerUp

            if (pointerData.m_PointerDown == firstRaycastGameObject)
            {
                OnLongPressEnd(pointerData); //todo: down对象触发OnLongPressEnd事件
                pointerData.m_LastDown = pointerData.m_PointerDown; //用于下次Down检测双击
            }
            else
            {
                pointerData.m_LastDown = null; //不触发LongPress, 就没后续双击
            }
            break;
        }

        pointerData.m_PointerDown = null;
        pointerData.m_PointerDrag = null;
        pointerData.m_Status = MyPointerEventData.Status.None;

        if (pointerData.m_PointerEnter != firstRaycastGameObject)
        {
            //todo: 在旧Enter节点上触发一次Exit
            //todo: 在抬起节点上触发一次Enter
        }

        m_TempRemovePointerDataList.Add(pointerData);
    }

    private void CheckTouchCancel(Touch touch)
    {
        var pointerData = GetPointerData(touch.fingerId);
        pointerData.m_Phase = touch.phase;
        pointerData.m_Delta = touch.position - pointerData.m_Position;
        pointerData.m_Position = touch.position;

        switch (pointerData.m_Status)
        {
        case MyPointerEventData.Status.None:
            OnPointerUp(pointerData); //todo: down对象触发OnPointerUp
            pointerData.m_LastDown = null; //不触发点击, 就没后续双击
            break;

        case MyPointerEventData.Status.Drag:
            //在前面已经触发过OnPointerUp
            if (null != pointerData.m_PointerDrag)
            {
                OnDragEnd(pointerData); //todo: drag对象触发OnDragEnd
            }
            pointerData.m_LastDown = null; //drag, 下次Down没双击
            break;

        case MyPointerEventData.Status.LongPress:
            OnPointerUp(pointerData); //todo: down对象触发OnPointerUp
            pointerData.m_LastDown = null; //不触发LongPress, 就没后续双击
            break;
        }

        pointerData.m_PointerDown = null;
        pointerData.m_PointerDrag = null;
        pointerData.m_Status = MyPointerEventData.Status.None;

        if (pointerData.m_PointerEnter != null)
        {
            //todo: 在旧Enter节点上触发一次Exit
        }

        m_TempRemovePointerDataList.Add(pointerData);
    }

    private MyPointerEventData GetPointerData(int pid)
    {
        var pointerData = m_PointerData[pid];
        if (null == pointerData)
        {
            pointerData = new MyPointerEventData(pid);
            m_PointerData[pid] = pointerData;
        }

        m_ActivePointerDataList.Add(pointerData);
        return pointerData;
    }

    protected GameObject GetFirstRaycastGameObject(Vector2 position)
    {
        m_FrameInvokeCount++;
        if (m_FrameInvokeCount > 1)
            return m_FirstRaycastGameObject;

        var evtSystem = EventSystem.current;
        if (null == m_TempPointerEventData)
            m_TempPointerEventData = new PointerEventData(evtSystem);

        m_TempPointerEventData.position = new Vector2(position.x, position.y);
        evtSystem.RaycastAll(m_TempPointerEventData, m_RaycastResultCache);

        if (m_RaycastResultCache.Count > 0)
        {
            for (int i = 0; i < m_RaycastResultCache.Count; ++i)
            {
                var go = m_RaycastResultCache[i].gameObject;
                if (null != go)
                {
                    m_FirstRaycastGameObject = go;
                    break;
                }
            }
            m_RaycastResultCache.Clear();
        }

        return m_FirstRaycastGameObject;
    }

}
